#include <stdio.h>
#include <string.h>
#include <assert.h>
#define MOD 256

const unsigned char * KSA(const  unsigned char *key, int key_len, unsigned char *S) {

    int i;
    int j;

    j = 0;

    for (i = 0; i < MOD; i++) {
        S[i] = i;
    }

    for( i = 0; i < MOD; i++) {

        j = (j + S[i] + key[i%key_len])%MOD;

        S[i] ^= S[j];
        S[j] ^= S[i];
        S[i] ^= S[j];

    }

    return S;
}

const unsigned char *PRGA(unsigned char *S,char *key, const unsigned char  *str, int str_len) {

    int i;
    int j;
    int k;
    int t;

    i = 0;
    j = 0;

    for ( k = 0; k < str_len; k++) {
        i = (i + 1) % MOD;
        j = (j + S[i]) % MOD;

        S[i] ^= S[j];
        S[j] ^= S[i];
        S[i] ^= S[j];
        
        t = S[(S[i] + S[j])%MOD];
        
        key[k] = t;
    }

    return key;
}


void rc4_crypt(const unsigned char  *t1,unsigned int str_len, 
               const unsigned char *key,unsigned int key_len,
               unsigned char *t2)
{

    int i;
    int j;
    int k;
    int t;

    unsigned char cipher_key;
    static unsigned char S[MOD];

    i = 0;
    j = 0;


    KSA( key, key_len, S);

    for ( k = 0; k < str_len; k++) {
        i = (i + 1) % MOD;
        j = (j + S[i]) % MOD;


      
        S[i] ^= S[j];
        S[j] ^= S[i];
        S[i] ^= S[j];
        
        cipher_key = S[(S[i] + S[j])%MOD];
        
        t2[k] = cipher_key ^ t1[k];
    }

}

void rc4_encrypt(const unsigned char *ptext, unsigned int str_len,
                 const  unsigned char  *key, unsigned int key_len, 
                 unsigned char *ctext) {
    rc4_crypt(ptext, str_len, key, key_len, ctext);
}
void rc4_decrypt(const unsigned char *ctext, unsigned int str_len,
                 const  unsigned char  *key, unsigned int key_len,
                 unsigned char *ptext) {
    rc4_crypt(ctext, str_len, key, key_len, ptext);
}

void print_testcase(int idx, const unsigned char  *ptext, const unsigned char *key) {
    printf("test case %d:\n", idx);
    printf("plaintext:%s\n", ptext);
    printf("key:%s\n", key);
}

void test3() {
    char plaintext[237] = "The libevent API provides a mechanism";
    const  char  *key       = "Linux Network Event-Driven Lib";

    print_testcase(3, plaintext, key);

    int klen = strlen( key );
    int plen = strlen( plaintext );

    unsigned char ciphertext[237] = {0};
    rc4_encrypt( plaintext, plen, key, klen, ciphertext);

    unsigned char cipher_str[575] = {0};

    size_t len = strlen(ciphertext);
    size_t i ;
    printf("%d\n", len);
    
    printf("ciphertext:\n");
    for ( i = 0;i < len; i++) {
        printf("%02X", ciphertext[i] );
        sprintf( cipher_str + 2 * i, "%02X", ciphertext[i]);
        if (i == len - 1)
            printf("\n");
    }

    if ( strcmp( cipher_str, "8F900EE6266AF225D82705D832E7D0F56ED6D256047116DF63554A89B263E38E7BDA2BA60C")) {
        printf("test case 3 encrypt error!!!\n");
    }


    
    unsigned char origtext[237] = {0};
    rc4_decrypt( ciphertext, plen, key, klen, origtext);
    
    printf("origtext:\n");
    for ( i = 0;i < plen; i++) {
        printf("%c", origtext[i] );
    }
}


void test2() {
    const char *plaintext = "Fuck America";
    const char  *key       = "Fuck";

    print_testcase(2, plaintext, key);


    int plen = strlen(plaintext);
    int klen = strlen(key);

    unsigned char ciphertext[13] = {0};
    rc4_encrypt( plaintext, plen, key, klen, ciphertext);

    char cipher_str[26] = {0};


    printf("ciphertext:\n");
    for (int i = 0;i < plen; i++) {
        printf("%02X", ciphertext[i] );
        sprintf( cipher_str + 2 * i, "%02X", ciphertext[i]);
        if (i == plen - 1)
            printf("\n");
    }


    if ( strcmp(cipher_str, "137021F3DC8343EEF038241E")) {
        printf("test case 2 encrypt failed!!!\n");
    }
    
    unsigned char origtext[13] = {0};
    rc4_decrypt( ciphertext, plen, key, klen, origtext);
    
    printf("origtext:\n");
    for (int i = 0;i < plen; i++) {
        printf("%c", origtext[i] );
    } 
}



void test1() {
    
    const char *plaintext = "Plaintext";
    const char  *key       = "Key";

    int plen = strlen(plaintext);
    int klen = strlen(key);

    char cipher_str[24] = {0};
    unsigned char ciphertext[12] = {0};
    rc4_encrypt( plaintext, plen, key, klen, ciphertext);

    
    printf("ciphertext:\n");
    for (int i = 0;i < plen; i++) {
        printf("%02X", ciphertext[i] );
        sprintf(cipher_str + 2 * i,"%02X", ciphertext[i]);
        if (i == plen - 1)
            printf("\n");
    }
    if(strcmp(cipher_str, "BBF316E8D940AF0AD3"))
        printf("test case 1 encrypt failed!!!\n");

    unsigned char origtext[12] = {0};
    rc4_decrypt( ciphertext, plen, key, klen, origtext);
    
    printf("origtext:\n");
    for (int i = 0;i < plen; i++) {
        printf("%c", origtext[i] );
    }
}

void test() {
    test1();
    test2();
    test3();
}
int main()
{   
    test();

    unsigned char plaintext[237] = "The libevent API provides a mechanism to execute a callback function when a specific event occurs on a file descriptor or after a timeout has been reached. Furthermore, libevent also support callbacks due to signals or regular timeouts.";
    const unsigned char  *key       = "Linux Network Event-Driven Lib";
    unsigned char ciphertext[237] = {0};
    unsigned char origtext[237] = {0};
    printf("%s\n", plaintext);

    int plen = strlen( plaintext );
    int klen = strlen( key );

    rc4_encrypt( plaintext, plen, key, klen, ciphertext);

    rc4_decrypt( ciphertext, plen,  key, klen, origtext);

    for (int i = 0;i < plen; i++) {
        printf("%02X", ciphertext[i]);
    }
    printf("\n");

    for (int i = 0;i < plen;i++)
        printf("%c", origtext[i]);
    printf("\norig len: %d\n", strlen(origtext));
    return 0;
}